[T4Scaffolding.Scaffolder(Description = "Creates handler for message")][CmdletBinding()]
param(
	[parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$MessageType,
    [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$HandlerTemplate,
    [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][hashtable]$HandlerModel,
    
    [parameter(Mandatory = $true)][string]$CommonProject,
    [parameter(Mandatory = $true)][string]$BackendProject,
    
    [string]$HandlerName,
    [string]$MethodName,
    
	[string]$Folder,
	[string]$Area,
    
    [string]$Project,            
	[string]$CodeLanguage,
	
    [string[]]$TemplateFolders,
	[ScriptBlock]$ClassUpdateCallback
)

if (!$CodeLanguage) { $CodeLanguage = Get-ProjectLanguage -Project $BackendProject }

function GetOrCreateHandlerProjectItemViaTemplate($pathExcludingExtension, $template) 
{	
	$projectItem = Get-ProjectItem "$pathExcludingExtension.$CodeLanguage" -Project $BackendProject

	if (!$projectItem) 
    {
		# The project item doesn't already exist, so create it via template
		$templateFile = Find-ScaffolderTemplate $template -TemplateFolders $TemplateFolders -Project $BackendProject -CodeLanguage $CodeLanguage -ErrorIfNotFound
        
		if ($templateFile) 
        {
			# Render it, adding the output to the Visual Studio project
			$backendNamespace = (Get-Project $BackendProject).Properties.Item("DefaultNamespace").Value
            
            $templateModel = @{
                BackendNamespace = $backendNamespace;
				HandlerName = $HandlerName; 
			}
            
			$wroteFile = Invoke-ScaffoldTemplate -Template $templateFile -Model $templateModel -Project $BackendProject -OutputPath $pathExcludingExtension
			
            if($wroteFile) 
            {
				Write-Host "Added message handler '$wroteFile'"
                
				$projectItem = Get-ProjectItem $wroteFile -Project $BackendProject
                
				if (!$projectItem) { throw "Created message handler $wroteFile, but could not find it as a project item" }
			} 
            else 
            {
				throw "Unable to create message handler $pathExcludingExtension.$CodeLanguage"
			}
		}		
	} 
    else 
    {
		Write-Verbose "Message handler $pathExcludingExtension.$CodeLanguage already exists. Skipping template rendering..."
	}
    
	return $projectItem
}

function GetClassFromProjectItem($projectItem) 
{
	if (!$projectItem.FileCodeModel) {
		Write-Warning "$($projectItem.Name) is not a valid code file or does not match the project language. Skipping database context update..."
		return
	}

	$classes = $projectItem.FileCodeModel.CodeElements | ? { $_.Kind -eq 1 }
	if(!$classes) {
		# No top-level class found. Try searching inside top-level namespaces
		$classes = $projectItem.FileCodeModel.CodeElements | ? { $_.Kind -eq 5 } | ForEach-Object { $_.Children | ? { $_.Kind -eq 1 } }
	}
	if(($classes | Measure-Object).Count -ne 1) {
		$path = $projectItem.Properties.Item("FullPath").Value
		throw "Found existing DbContext file $path, but can't find the class it contains. Ensure it contains valid code and a single top-level class, or delete the file so it can be recreated."
	}
	return $classes
}

function GetGenericType($codeType)
{
    $typeStr = $codeType.FullName

    $A = $typeStr.IndexOf("<")
    $B = $typeStr.IndexOf(">")

    return $typeStr.Substring($A+1, $B-$A-1)
}

function GetResponseType($requestType)
{
    $IRequestCodeType = $requestType.ImplementedInterfaces | where { $_.Name -eq "IRequest" }
    
    return Get-ProjectTYpe (GetGenericType $IRequestCodeType) -Project $CommonProject
}

# default HandlerName is Common
if (!$HandlerName) { $HandlerName = "Common"; }


$requestType = Get-ProjectType $MessageType -Project $CommonProject
$responseType = GetResponseType $requestType -Project $CommonProject


# Determine where the output will go
$outputPath = Join-Path Handlers ($HandlerName + "Handler")

if ($Folder) {
	$outputPath = Join-Path $Folder $outputPath
}

# Find or create Message Handler class, then add a new method handler
$projectItem = GetOrCreateHandlerProjectItemViaTemplate $outputPath "MessageHandler"

if ($projectItem) 
{
	if (!$ClassUpdateCallback) 
    {
		$ClassUpdateCallback = 
        {
			param($class, $modelType, $codeLanguage)			
            
            # default name of method is same as name of request
            if (!$MethodName) { $MethodName = $requestType.Name }
			
            $nameComparisonType = if ($codeLanguage -eq "vb") { [StringComparison]::OrdinalIgnoreCase } else { [StringComparison]::Ordinal }			
            $existingProperty = $class.Members | ?{ [String]::Equals($_.Name, $MethodName, $nameComparisonType) }
            
            $HandlerModel.RequestType = $requestType.FullName
            $HandlerModel.ResponseType = $responseType.FullName     
            $HandlerModel.MethodName = $MethodName
            
            if (!$existingProperty)
            {
                Add-ClassMemberViaTemplate -CodeClass $class -Template $HandlerTemplate -Model $HandlerModel -TemplateFolders $TemplateFolders
                
				Write-Host "Added '$MethodName' to message handler '$outputPath.$CodeLanguage'"
			}
            else 
            {
				Write-Warning "$outputPath.$CodeLanguage already contains '$MethodName'! Skipping..."
			}
		}
	}

	$class = GetClassFromProjectItem $projectItem
	if ($class) 
    {
		. $ClassUpdateCallback $class $foundModelType $CodeLanguage
	}
}